package com.blog4j.user.aop;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.json.JSONUtil;
import com.blog4j.common.annotation.OperationLog;
import com.blog4j.common.enums.ModuleTypeEnum;
import com.blog4j.common.enums.OperationTypeEnum;
import com.blog4j.common.enums.LogResultEnum;
import com.blog4j.common.exception.Blog4jException;
import com.blog4j.common.exception.InvalidRequestException;
import com.blog4j.common.model.LogVo;
import com.blog4j.common.utils.CommonUtil;
import com.blog4j.common.utils.OperationLogHolder;
import com.blog4j.common.utils.RealIpUtil;
import com.blog4j.user.component.AsyncService;
import com.blog4j.user.entity.UserEntity;
import com.blog4j.user.mapper.UserMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @author 98k灬
 * @version v1.0.0
 * @Description : 功能描述
 * @Create on : 2024/8/18 14:30
 **/
@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Component
@Aspect
public class LogAspect {
    private final UserMapper userMapper;
    private final AsyncService asyncService;

    @Pointcut("@annotation(com.blog4j.common.annotation.OperationLog)")
    public void log() {}

    @AfterThrowing(pointcut = "log()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        String errMsg = e.getMessage();
        if (e instanceof Blog4jException) {
            Blog4jException blog4jException = (Blog4jException) e;
            errMsg = blog4jException.getErrMsg();
        } else if (e instanceof InvalidRequestException) {
            InvalidRequestException invalidRequestException = (InvalidRequestException) e;
            errMsg = invalidRequestException.getErrMsg();
        }

        log.error("接口异常: [{}]", errMsg);
        LogVo logVo = OperationLogHolder.getLogVo();
        logVo.setResult(LogResultEnum.FAIL.getResult()).setErrMsg(errMsg);
        OperationLogHolder.setLogVo(logVo);
    }

    @Around("log()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        LogVo logVo = getAnnotationInfo(method);
        logVo.setParam(JSONUtil.toJsonStr(joinPoint.getArgs()))
                .setResult(LogResultEnum.SUCCESS.getResult());
        // 方法名
        String methodName = joinPoint.getSignature().getName();
        logVo.setMethodName(methodName);

        // 得到request
        HttpServletRequest request = ((ServletRequestAttributes)
                Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        logVo.setIpAddress(RealIpUtil.getRealIp(request)).setUrl(request.getRequestURL().toString());
        UserEntity userInfo = getUserInfo();
        if (Objects.nonNull(userInfo)) {
            logVo.setUserId(userInfo.getUserId()).setUserName(userInfo.getUserName());
        }

        String className = joinPoint.getTarget().getClass().getName();
        logVo.setClassName(className)
                .setTime(System.currentTimeMillis() - startTime)
                .setCreateTime(CommonUtil.getCurrentDateTime());
        OperationLogHolder.setLogVo(logVo);
        return joinPoint.proceed();
    }

    @After("log()")
    public void doAfter(JoinPoint joinPoint) {
        LogVo logVo = OperationLogHolder.getLogVo();
        OperationLogHolder.removeLogVo();
        asyncService.addLog(logVo);
    }

    private LogVo getAnnotationInfo(Method method ) {
        LogVo log = new LogVo();
        OperationLog annotation = method.getAnnotation(OperationLog.class);
        if (annotation != null) {
            OperationTypeEnum operationTypeEnum = annotation.operationType();
            ModuleTypeEnum module = annotation.module();
            String description = annotation.description();
            log.setModuleType(module.getType())
                    .setOperationType(operationTypeEnum.getType())
                    .setDescription(description);
        }
        return log;
    }

    private UserEntity getUserInfo() {
        String userId = "";
        try {
            userId = StpUtil.getLoginIdAsString();
            return userMapper.selectById(userId);
        } catch (Exception exception) {
            log.warn("user not login");
        }
        return null;
    }
}
